package com.example.demo.Security;

import com.example.demo.MyPasswordEncoder;
import com.example.demo.Service.userInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.config.annotation.web.builders.*;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;

/**
 * author 陈伯瑞
 * date 2020/5/18 0018 10:00
 * version 1.0
 **/

public class userSecurity {
    /**
     *
     */


    @Configuration
    public  static  class userSecurityConfig extends WebSecurityConfigurerAdapter {
        @Resource
        private userInfoService service;
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(service).passwordEncoder(new MyPasswordEncoder());
        }

        /**
         * 前台安全过滤链配置。
         * <p></p>
         * 这里我们关注另一个问题，异常问题。
         * <p>
         * Spring Security主要分两种：{@link AuthenticationException}认证异常、{@link AccessDeniedException}访问拒绝异常，它们由{@link ExceptionTranslationFilter}捕获处理。<br/>
         * 具体逻辑看{@link ExceptionTranslationFilter
         * #handleSpringSecurityException(HttpServletRequest, HttpServletResponse, FilterChain, RuntimeException)}
         * <p></p>
         * 由上面的{@code handleSpringSecurityException}方法源码可知：<br>
         * 认证异常被捕获后交由{@link AuthenticationEntryPoint}处理；<p>
         * 访问拒绝异常被捕获后由{@link AccessDeniedHandler}处理。<p>
         * 注意的是，如果用户没有认证，即是匿名用户，抛出{@link AccessDeniedException}时会交由{@link AuthenticationEntryPoint}处理。
         * <p></p>
         * <p>
         * {@link WebSecurityConfigurerAdapter#getHttp()}查看这个方法的源码，了解默认调用了{@link HttpSecurity#exceptionHandling()}方法；<br>
         * {@link HttpSecurity#exceptionHandling()}方法会添加{@link ExceptionHandlingConfigurer}配置器；<br>
         * {@link ExceptionHandlingConfigurer} 配置{@link AuthenticationEntryPoint}和{@link AccessDeniedHandler}，把它们赋值给{@link ExceptionTranslationFilter}，最后把{@link ExceptionTranslationFilter}加入安全过滤链。<p></p>
         *
         * @see LoginUrlAuthenticationEntryPoint
         * @see AccessDeniedHandlerImpl
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 不指定path,本安全过滤链会匹配所有请求。
            http
                    .authorizeRequests()
                    .antMatchers("/index","/user/create").permitAll()// 首页放行
                    .antMatchers("/css/**","/js/**","/update").permitAll()// 首页放行
                    .anyRequest().hasAnyAuthority("user").and()
                    .formLogin()
                    .loginPage("/index").permitAll()
                    .defaultSuccessUrl("/userinfo").and()
                    .logout()
                    .logoutUrl("/loginout").permitAll()
                    .logoutSuccessUrl("/index").and()
                    // 自定义访问拒绝异常处理逻辑
                    .exceptionHandling().accessDeniedHandler(userSecurityConfig::accessDeniedHandle)

            ;
        }

        /**
         * @see AccessDeniedHandlerImpl#handle(HttpServletRequest, HttpServletResponse, AccessDeniedException)
         */
        private static void accessDeniedHandle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
            request.setAttribute(WebAttributes.ACCESS_DENIED_403,
                    accessDeniedException);
            response.setStatus(HttpStatus.FORBIDDEN.value());
            response.setCharacterEncoding(Charset.defaultCharset().displayName());// 解决中文乱码
            response.addHeader("Content-Type", MediaType.TEXT_HTML_VALUE);
            response.getWriter().write("你的权限不够");
        }
    }


}
